/*
 * Miccsnookersim, a snooker simulation with AI
 * Copyright (C) 2009 Enno Ruijters, Frans van den Heuvel, Yannick Thimister,
 * Ali B. Ozmen, Esther Verhoef, Achim Leydecker
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

import java.io.*;

public class LogPlayer implements Player
{
	private DataInputStream inputStream;
	private String name;

	/**
		Creates an instance of the AI
	*/
	public LogPlayer(String logFileName, Table t, String playerName)
	{
		try {
			InputStream tmp = new FileInputStream(logFileName);
			inputStream = new DataInputStream(tmp);
		} catch (FileNotFoundException e) {
			System.err.println("File not found: " + logFileName);
			System.exit(1);
		} catch (SecurityException e) {
			System.err.println("Not allowed to open log file");
			System.exit(1);
		}
		name = playerName;
	}

	private LogPlayer()
	{
	}

	public LogPlayer clone()
	{
		LogPlayer ret = new LogPlayer();
		ret.inputStream = this.inputStream;
		ret.name = this.name;
		return ret;
	}

	public void setName(String newName)
	{
		name = newName;
	}

	private void invalidLog(String message)
	{
		System.err.println("Invalid log file: " + message);
		System.exit(1);
	}

	public Vector readVector()
		throws IOException
	{
		double components[];
		byte tmp;

		try {
			tmp = inputStream.readByte();
			if (tmp != 'V')
				invalidLog("Expected vector type, got " + tmp);
		} catch (EOFException e) {
			return null;
		}
		tmp = inputStream.readByte();
		if (tmp != ':')
			invalidLog("Vector header not followed by ':'");
		components = new double[3];
		components[0] = inputStream.readDouble();
		components[1] = inputStream.readDouble();
		components[2] = inputStream.readDouble();
		tmp = inputStream.readByte();
		if (tmp != '\n')
			invalidLog("Vector not followed by newline");
		return new Vector(components[0], components[1], components[2]);
	}

	public String readMessage()
		throws IOException
	{
		byte type, tmp, retArray[];
		int length = 0;

		try {
			type = inputStream.readByte();
		} catch (EOFException e) {
			return null;
		}
		if (type != 'T')
			invalidLog("Expected header T, got " + ((char) type));
		tmp = inputStream.readByte();
		if (tmp != '(')
			invalidLog("Header T not followed by length");
		do {
			tmp = inputStream.readByte();
			if ((tmp < '0' || tmp > '9') && tmp != ')')
				invalidLog("Invalid characters in message length (" + ((char) tmp) + ")");
			if (tmp != ')')
				length = length * 10 + (tmp - '0');
		} while (tmp != ')');
		tmp = inputStream.readByte();
		if (tmp != ':')
			invalidLog("Message length not followed by ':'");
		tmp = inputStream.readByte();
		if (tmp != ' ')
			invalidLog("Message not preceded by blank space");
		length -= 2;
		retArray = new byte[length];
		inputStream.readFully(retArray);
		tmp = inputStream.readByte();
		if (tmp != '\n')
			invalidLog("Message not followed by newline");
		return new String(retArray);
	}

	public synchronized Cue getShot()
	{
		Cue ret = new Cue();
		String tmpMessage;
		Vector tmpVector;

		try {
			tmpMessage = readMessage();
			if (tmpMessage == null) {
				System.out.println("End of log file");
				while (true) {
					try {
						Thread.sleep(100);
					} catch (Exception e) {
					}
				}
			}
			if (!(tmpMessage.equals("Force:")))
				invalidLog("Expected force header, got '"
			                   + tmpMessage);
			tmpVector = readVector();
			if (tmpVector == null)
				throw new EOFException();
			ret.setForce(tmpVector);
			tmpMessage = readMessage();
			if (tmpMessage == null)
				throw new EOFException();
			if (!(tmpMessage.equals("Position:")))
				invalidLog("Expected position header, got '"
			                   + tmpMessage);
			tmpVector = readVector();
			if (tmpVector == null)
				throw new EOFException();
			ret.setPosition(tmpVector);
			return ret;
		} catch (EOFException e) {
			invalidLog("Unexpected end of log file");
			throw new RuntimeException();
		} catch (IOException e) {
			e.printStackTrace();
		}
		throw new RuntimeException("Magic has happened");

	}

	public synchronized void replaceCueBall(Table t, Ball cueBall)
	{
		String tmpMessage;
		Vector tmpVector;
		try {
			tmpMessage = readMessage();
			if (tmpMessage == null) {
				System.out.println("End of log file");
				while (true) {
					try {
						Thread.sleep(100);
					} catch (Exception e) {
					}
				}
			}
			if (!(tmpMessage.equals("Ball placed:")))
				invalidLog("Expected ballPlaced header, got '"
			                   + tmpMessage);
			tmpVector = readVector();
			if (tmpVector == null)
				throw new EOFException();
			cueBall.setPosition(tmpVector);
			t.replaceBall(cueBall);
		} catch (EOFException e) {
			invalidLog("Unexpected end of log file");
			throw new RuntimeException();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public String getName()
	{
		return "Logged game " + name;
	}

	public synchronized void terminate()
	{
		try {
			inputStream.close();
		} catch (Exception e) {
		}
	}

	public void setTable(Table newTable)
	{
		throw new IllegalStateException();
	}
}
